//---------------------------------------------------------------------
// Copyright (C) 2010 - Cobalt Solution / Sebastien Leon              |
//---------------------------------------------------------------------
// This software is under dual licence: commercial licence + GPL v3   |
// To keep closed your modification of this software, you need to     |
// contact me to obtain a commercial licence.                         |
// Any other derivated work should remain Open Source.                |
// Contact me for a licence, a question or if you want to commit      |
// your code to the main tree: seb at cobaltsolutions dot info        |
//---------------------------------------------------------------------

//#include <QList>
#include <QDesktopWidget>
#include <QApplication>
#include <QMouseEvent>
#include <QUrl>

#include "LoomView.h"
#include "mainForm.h"
#include "Core.h"

#define DROP_SHIFT 5

LoomView::LoomView ( QWidget * parent ):
  QGraphicsView (parent),
  m_dropFeedBack(NULL  ),
  mp_mainForm   (NULL  ),
  m_startLoom   (NULL  ),
  m_action      (e_none)
{
  // Apply a scale factor according the resolution:
//  QPaintDevice* device( QApplication::desktop()->screen( 0 ) );
//  float x = device->logicalDpiX();// / Loom::Fc / 2.54; // points per cm
//  float y = device->logicalDpiY();// / Loom::Fc / 2.54; // points per cm
//  scale(x,y);
}
//----------------------------------------------------
void LoomView::SetForm ( MainForm& main )
{
  mp_mainForm = &main;
  setScene    (&main.LoomScene());
  setAlignment(Qt::AlignLeft | Qt::AlignTop);
  setDragMode (QGraphicsView::ScrollHandDrag);
  setMouseTracking (true);
}
//----------------------------------------------------
void LoomView::resizeEvent ( QResizeEvent* e )
{
  // we move the allLoom object at the bottom-left position
  m_allLooms.setPos( 10, viewport()->height() - 10 -m_allLooms.boundingRect().height());
  // Normal process...
  QGraphicsView::resizeEvent(e);
}
//----------------------------------------------------
void LoomView::mousePressEvent  (QMouseEvent *event)
{
  if (event->buttons() == Qt::LeftButton)
  {
    // Let's change the drag mode & the cursor
    QPointF scenePos    = mapToScene(event->pos());
    QGraphicsItem* item = scene()->itemAt(scenePos);
    setDragMode(item?QGraphicsView::NoDrag:QGraphicsView::ScrollHandDrag);
    if (item)
    {
      if (CanEditItems())
      {
        Loom* loom = dynamic_cast<Loom*>(item);
        if (loom)
        {
          m_startLoom = loom;
          m_startPos  = scenePos;
          if (loom->IsInCaption(scenePos))
          {
            m_startValue = loom->Pos();
            m_action     = e_move;
          }
          else if (loom->IsInResize(scenePos))
          {
            m_startValue = QPoint(loom->Size().width(), loom->Size().height());
            m_action     = e_resize;
          }
          else
          {
            // nothing particular
          }
        }
      }
    }
    else QGraphicsView::mousePressEvent  (event);
  }
}
//----------------------------------------------------
void LoomView::mouseMoveEvent   (QMouseEvent *event)
{
  if (m_action == e_none && event->buttons() == Qt::NoButton) // MOUSE TRACKING
  {
    // Let's change the drag mode & the cursor
    QPointF scenePos    = mapToScene(event->pos());
    QGraphicsItem* item = scene()->itemAt(scenePos);
    setDragMode(item?QGraphicsView::NoDrag:QGraphicsView::ScrollHandDrag);
    if (CanEditItems())
    {
      const Loom* loom = dynamic_cast<const Loom*>(item);
      // Set move cursor:
      if (loom && loom->IsInCaption(scenePos))
        setCursor(Qt::SizeAllCursor); 
      else if (loom && loom->IsInResize(scenePos))
        setCursor(Qt::SizeFDiagCursor); 
      else 
        unsetCursor();
    }
  }
  else
  {
    if (m_action == e_none)
    {
      QGraphicsView::mouseMoveEvent  (event);
    }
    else
    {
      Q_ASSERT(CanEditItems());
      QPointF scenePos = mapToScene(event->pos());
      QPointF delta    = scenePos - m_startPos;
      if (m_action == e_move)
      {
        m_startLoom->SetPos(m_startValue + delta);
      }
      else if (m_action == e_resize)
      {
        QPointF newSz(m_startValue + delta);
        m_startLoom->SetSize(QSizeF(newSz.x(), newSz.y()));
      }
    }
  }
}
//----------------------------------------------------
void LoomView::mouseReleaseEvent(QMouseEvent *event)
{
  if (m_action != e_none)
  {
    // Last event: inform the Core of this modif (not really clean, we break here the const model...)
    mp_mainForm->ValidLoomPosSizeChange(m_startLoom);
    m_action    = e_none;
    m_startLoom = NULL;
  }
  QGraphicsView::mouseReleaseEvent  (event);
}
//----------------------------------------------------
void LoomView::mouseDoubleClickEvent(QMouseEvent *e)
{
  // find the item below the mouse:
  QGraphicsItem *item = itemAt(e->pos());
  if (item && mp_mainForm)
  {
    mp_mainForm->DblClickItem(item);
  }
}
//----------------------------------------------------
bool LoomView::CanEditItems()
{
  return mp_mainForm ? !mp_mainForm->IsReadOnly() : false;
}
//----------------------------------------------------
void LoomView::dragEnterEvent ( QDragEnterEvent* event )
{
  if ( event->mimeData()->hasUrls() )
  {
    if (m_dropFeedBack) delete m_dropFeedBack; // useful for debug. Must be NULL usually
    m_dropFeedBack = new DropFeedBack(*this);
    event->acceptProposedAction();
  }
  scene()->addItem(&m_allLooms);
}  
//----------------------------------------------------
void LoomView::dragLeaveEvent ( QDragLeaveEvent* event )
{
  Q_UNUSED(event);
  delete m_dropFeedBack; m_dropFeedBack = NULL;
  scene()->removeItem(&m_allLooms);
}
//----------------------------------------------------
void LoomView::dragMoveEvent  ( QDragMoveEvent*  event )
{
  Q_ASSERT(m_dropFeedBack);
  m_dropFeedBack->UpdateLoom (event->pos());
  m_allLooms.Dragging(&m_allLooms == itemAt(event->pos()));
}
//----------------------------------------------------
void LoomView::dropEvent ( QDropEvent* event )
{
  bool res = false;
  if ( event->mimeData()->hasUrls() && (m_allLooms.IsOn() || m_dropFeedBack && m_dropFeedBack->LoomTarget() && m_dropFeedBack->LoomTarget()->IsEnabled()) )
  {
    QList<QUrl> lst (event->mimeData()->urls());
    // we will create the resources...
    for (int n = 0 ; n < lst.count() ; ++n)
    {
      QString path(lst[n].toLocalFile());  
      //qDebug() << "URL " << n << " : " << path;
      if (!path.isEmpty())
      {
        QFileInfo nfo(path);
        QString filename (nfo.baseName());
        if (mp_mainForm) mp_mainForm->InitiateTransfer(nfo, m_allLooms.IsOn() ? NULL : m_dropFeedBack->LoomTarget());
      }
    }
  }
  // clean feedback effect
  delete m_dropFeedBack; m_dropFeedBack = NULL;
  scene()->removeItem(&m_allLooms);

  // Provide user feed back:
  if (res) event->acceptProposedAction();
}
//----------------------------------------------------
//----------------------------------------------------
DropFeedBack::DropFeedBack (LoomView& lview):
  QGraphicsRectItem( NULL),
  m_anim  (this,"opacity"),
  mr_lview         (lview),
  mp_l             ( NULL)
{
  QPen pen;
  pen.setWidth(5);
  pen.setColor(Qt::blue);
  setPen      (pen);
  setBrush    (Qt::NoBrush);
  setZValue   (-1);
  mr_lview.scene()->addItem(this);
  m_anim.setDuration(600);
  m_anim.setEasingCurve(QEasingCurve::InOutQuad);
  m_anim.setDuration   (1000);
  m_anim.setLoopCount  (-1);
  m_anim.setKeyValueAt (0.5, 0.1);
  m_anim.setStartValue (0.7);
  m_anim.setEndValue   (0.7);
  setOpacity(0.7);
}
//----------------------------------------------------
bool DropFeedBack::UpdateLoom (const QPoint& pos)
{
  // Find item below this pos:
  QGraphicsItem* p_item = mr_lview.itemAt(pos);
  // a Loom ?
  Loom* newL = p_item ? dynamic_cast<Loom*>(p_item) : NULL;

  if (newL != mp_l) // Internal State changed !
  {
    mp_l = newL;
    if (mp_l)
    {
      if (mp_l->IsEnabled())
      {
        // Move itself around the loom
        show();
        m_anim.start();
        setRect(mp_l->boundingRect().translated(mp_l->pos()).adjusted (-DROP_SHIFT,-DROP_SHIFT,+DROP_SHIFT,+DROP_SHIFT));
      }
    }
    else // No loom found...
    {
      m_anim.stop();
      hide();
    }
  }

  return (bool) mp_l;
}
//----------------------------------------------------
